home *** CD-ROM | disk | FTP | other *** search
- From: "Oliver Laumann" <talcott!seismo!unido!tub!net>
- Subject: dial -- a state transition controlled communications program
- Newsgroups: mod.sources
- Approved: jpn@panda.UUCP
-
- Mod.sources: Volume 3, Issue 118
- Submitted by: "Oliver Laumann" <talcott!seismo!unido!tub!net>
-
- #! /bin/sh
- # This is a shell archive, meaning:
- # 1. Remove everything above the #! /bin/sh line.
- # 2. Save the resulting text in a file.
- # 3. Execute the file with /bin/sh (not csh) to create the files:
- # README
- # dial.1
- # dial.c
- # This archive created: Thu Feb 6 15:15:02 1986
- export PATH; PATH=/bin:$PATH
- echo shar: extracting "'README'" '(1552 characters)'
- if test -f 'README'
- then
- echo shar: will not over-write existing file "'README'"
- else
- cat << \SHAR_EOF > 'README'
- Dial is a (4.xBSD-only) communications program for tty lines. It is
- similar to tip(1), though it doesn't support as many options as tip and
- no file transfer. In addition to interactive access to a remote
- computer, dial can operate in a non-interactive way using a
- user-supplied state transition script. This is useful for accessing
- certain `server logins' on remote machines without human interaction
- and in cases where the connect/login procedure for a remote computer is
- complex, e.g. when a remote system is accessed through a PABX, LAN, or
- terminal selector.
-
- Compile dial using the following command:
- cc -o dial dial.c -ltermcap
- (the termcap library is solely used to read entries from /etc/remote).
-
- `dial.1' contains the manual page for dial.
-
- `example' contains a sample state transition script that can be
- interpreted by dial. It is probably of no particular use for you; we
- are using it here to connect to a server login on a remote UNIX machine
- through our local PABX (see the comments in `example'). But you can
- get an idea how a dial script looks like.
-
- Dial makes use of select(2), remote(5), and the Berkeley-UNIX tty
- driver. Thus, it is probably impossible to port it to non-BSD
- systems. I don't know if it will run under 4.3BSD (we haven't
- got 4.3 here, yet).
-
- Please mail bug-reports and useful modifications to:
-
- ...ihnp4!seismo!unido!tub!net or net@DB0TUI6.BITNET
- ...!mcvax!unido!tub!net
-
- Regards,
- Oliver Laumann
- Technical University of Berlin,
- Communications and Operating Systems Research Group.
-
- SHAR_EOF
- if test 1552 -ne "`wc -c < 'README'`"
- then
- echo shar: error transmitting "'README'" '(should have been 1552 characters)'
- fi
- fi # end of overwriting check
- echo shar: extracting "'dial.1'" '(7835 characters)'
- if test -f 'dial.1'
- then
- echo shar: will not over-write existing file "'dial.1'"
- else
- cat << \SHAR_EOF > 'dial.1'
- .TH DIAL 1 "23 January 1986"
- .UC 4
- .SH NAME
- dial \- connect to a remote system using a state transition script
- .SH SYNOPSIS
- .B dial
- [
- .B \-v
- ] [
- .B \-p
- ] [ \-l\f2line\fP
- ] [script-file [ arguments... ]]
- .SH DESCRIPTION
- .I Dial
- establishes a connection to a remote machine using
- a user-supplied state transition script.
- .I Dial
- is mainly used to communicate with remote server-logins in a
- non-interactive way, or for interactive sessions on a remote machine
- when the login/logout procedures should be carried out automatically.
- .PP
- The
- .B \-l\f2line\fP
- parameter specifies the line to be opened to establish the connection.
- If it is a relative pathname,
- .I dial
- prepends the string
- .I ``/dev/''
- to the specified line.
- If the line does not exist,
- .I dial
- interprets
- .I line
- as the system name of the remote machine and uses the file /etc/remote
- to find out how to reach the system and how the line parameters
- (e.g. baud rate) should be set; refer to
- .IR remote (5)
- for a full description.
- .PP
- The
- .B \-v
- option causes
- .I dial
- to print on standard output diagnostic information for each
- state transition.
- This can be helpful for debugging
- .I dial
- scripts.
- .PP
- .I Script-file
- contains the state transition table; if
- .I script-file
- is `\-', standard input is used.
- If
- .I script-file
- cannot be opened, the value of the environment variable ``DIALDIR''
- (if present) is prepended to the file name.
- If this also fails, the path ``/usr/local/lib/dial/'' is prepended to
- the specified file name.
- .PP
- Lines in the script beginning with `#' are treated as comment lines.
- Comment lines and blank lines are ignored.
- All occurrences of `$\f2n\fP' in the script are substituted by the \f2n\fPth
- optional argument
- from the command line; the first argument is `$0'.
- `$*' is substituted by the concatenation of all arguments separated
- by blanks.
- All occurrences of `${\f2symbol\fP}' are replaced by the value of
- the environment variable
- .I symbol.
- If
- .I symbol
- begins with `~' or `/', it is interpreted as the name of a file,
- and the contents of the file is interpolated into the
- script (\f2dial\fP performs
- .IR csh (1)-style
- expansion of `~' and `~user').
- .PP
- The script file contains a list of state definitions; each definition
- is of the form
- .RS
- .HP
- .nf
- state-name send-string [[time-out][,delay]]
- next-state pattern [output-string]
- next-state pattern [output-string]
- \0\0\0...
- .fi
- .RE
- .PP
- The first line of each state definition starts in column 0; the following
- lines are indented by spaces or tabs.
- .I
- State-name
- is the name of the state;
- .I send-string
- is a string that is sent to the remote system whenever the state is entered.
- When a state has been entered,
- .I dial
- receives data from the remote system until the characters read
- so far match one of the
- .I patterns
- listed in the state definitions.
- If a match occurs, the optional
- .I output-string
- is sent to the standard output, and then the corresponding
- .I next-state
- is entered.
- The order of the
- .I "next-state\-pattern"
- pairs is significant.
- .PP
- When the
- .B \-p
- options is given, the
- .I output-string
- field of each rule is ignored.
- Instead, all characters received from the remote system are sent
- to the standard output.
- .PP
- When a time-out occurs while receiving from the remote system,
- a transition to the state
- .B exit
- is performed (if not specified otherwise).
- The default time-out for each state is 10 seconds; it can be changed
- as described below.
- The optional
- .I time-out
- field in each state definition can be used to assign a different
- time-out value to this state.
- Likewise, the
- .I .delay
- field can be used to specify a delay (measured in seconds) to be
- performed before the
- .I send-string
- is transmitted.
- Both fields are optional.
- Thus,
- .B ``,1.5''
- means ``use the default time-out and a delay of 1.5 seconds''.
- .PP
- Each time the symbol `\\<' or `\\?' is encountered in the
- .I send-string
- field of a state definition,
- .I dial
- reads one input line from standard input and replaces the symbol
- with that line (not including the terminating newline).
- In case of `\\?', the read is performed in CBREAK mode with all
- special characters as well as echo turned off.
- This is useful when passwords should be interpolated into the
- data sent to the remote machine, but should not appear directly
- in the script.
- .PP
- .I Pattern
- is a simple character string; `.' within a pattern matches any character.
- The special pattern `*' matches anything (useful for catch-all rules);
- the pattern `#' denotes
- .IR timeout ,
- that is, the corresponding
- .I next-state
- is entered when a time-out occurs.
- In
- .IR output-string,
- the symbol `&'
- denotes the string that matched the corresponding pattern.
- This mechanism is used to print on standard output characters
- received from the remote system or diagnostic messages.
- The special interpretation of `.', `#', `*', `&', `$', `\\<', and `\\?' is
- suppressed when the symbol is preceded by `\\' (backslash).
- .PP
- .IR Send-string ,
- .IR pattern ,
- and
- .I output-string
- can be surrounded by double-quotes.
- This is useful when blanks should be included or to denote an
- empty string.
- The symbol `\\r' can be used to denote
- .IR "carriage return" ,
- `\\n' stands for
- .IR "line feed" .
- In addition, a `\\' followed by one to three octal digits stands for the
- character whose ASCII code is given by those digits.
- .PP
- When
- .I dial
- is called, a transition to the first state listed in the script file
- is performed.
- A transition to the special state
- .B exit
- causes
- .I dial
- to drop the connection and terminate.
- A transition to the state
- .B user
- causes
- .I dial
- to enter interactive mode.
- In this mode, all typed characters are transmitted to the remote machine,
- while all characters received from the remote machine are sent to
- standard output (that is,
- .I dial
- acts as if you had been connected to the remote machine using
- .IR tip (1)).
- .PP
- If no script file is given, or if the specified script-file does not
- contain any state definitions,
- .I dial
- performs a transition to the state
- .BR user ,
- that is, opens an interactive session.
- .PP
- If the special
- .I "escape character"
- (usually `~') followed by carriage return or space
- is typed in interactive mode,
- .I dial
- sends the
- .I "prompt string"
- (usually `:')
- to standard output and reads from standard input the name of a new state.
- If a valid state name has been entered,
- .I dial
- leaves the interactive mode and enters the given state.
- .PP
- If the
- .I "escape character"
- is followed by either `.' or the end-of-file character (usually `^D'),
- .I dial
- enters the state
- .B exit
- immediately, that is, closes the connection and terminates.
- .I Dial
- can be suspended by typing
- .I escape
- followed by the suspend process character (usually `^Z').
- .I Escape
- followed by `!' forks a shell;
- .I escape
- followed by `?' prints a summary of available
- .I escape
- commands.
- .PP
- Lines in the script file of the form
- .IP
- keyword = value
- .PP
- are used to assign values to certain parameters.
- Valid keywords are
- .B timeout
- (the default time-out to be used),
- .B delay
- (the default delay),
- .B line
- (the line to be opened),
- .B escape
- (the
- .IR "escape character" ),
- and
- .B prompt
- (the
- .IR "prompt string" ).
- The value for
- .B line
- is overridden by the
- .B \-l\f2line\fP
- option.
- .SH EXAMPLES
- The following simple script prints on standard output each character
- received from /dev/tty0.
- It terminates when a `.' is received or when a timeout of 0.5 second
- occurs.
- .PP
- .RS
- .nf
- # Simple example for a dial script:
- line=tty0
- loop "" .5
- exit \\. "Done.\\n"
- exit # "Got time-out.\\n"
- loop * &
- .fi
- .RE
- .ta \w'/usr/spool/uucp/LCK..*\0\0\0'u
- .SH FILES
- .nf
- /usr/local system-wide \f2dial\fP scripts
- .br
- ${DIALDIR} private \f2dial\fP scripts
- .br
- /usr/spool/uucp/LCK..* lock file for \f2uucp\fP and \f2tip\fP
- .fi
- .SH "SEE ALSO"
- tip(1C)
- .SH AUTHOR
- Oliver Laumann
- .SH BUGS
- The number of characters actually matched by the pattern `*'
- is not deterministic.
- SHAR_EOF
- if test 7835 -ne "`wc -c < 'dial.1'`"
- then
- echo shar: error transmitting "'dial.1'" '(should have been 7835 characters)'
- fi
- fi # end of overwriting check
- echo shar: extracting "'dial.c'" '(21088 characters)'
- if test -f 'dial.c'
- then
- echo shar: will not over-write existing file "'dial.c'"
- else
- cat << \SHAR_EOF > 'dial.c'
- /* dial -- dial a remote computer using a state transition table
- *
- * Copyright (c) 1986, Oliver Laumann, Technical University of Berlin.
- * Not derived from licensed software.
- *
- * Permission is granted to freely use, copy, modify, and redistribute
- * this software, provided that no attempt is made to gain profit from it,
- * the author is not construed to be liable for any results of using the
- * software, alterations are clearly marked as such, and this notice is
- * not modified.
- */
-
- #include <stdio.h>
- #include <pwd.h>
- #include <sys/ioctl.h>
- #include <sys/time.h>
- #include <sys/file.h>
- #include <sys/errno.h>
- #include <sys/signal.h>
- #include <sys/types.h>
- #include <sys/stat.h>
-
- #define TIMEOUT 10
- #define DELAY 0
- #define BUFLEN 1024
- #define EXIT ((struct state *)0)
- #define LOCKFN "/usr/spool/uucp/LCK..%s"
- #define DIALLIB "/usr/local/lib/dial"
- #define REMOTE "/etc/remote"
-
- #define FULL 0 /* match() return values */
- #define PART 1
- #define NONE 2
-
- #define ISSPACE(x) (x == ' ' || x == '\t')
- #define ISOCT(x) (x >= '0' && x <= '7')
- #define ISDEC(x) (x >= '0' && x <= '9')
-
- char *script;
- char *line;
- int escape = '~';
- char *prompt = "\r\n:";
- struct timeval timeout = {TIMEOUT, 0};
- struct timeval delay = {DELAY, 0};
- char obuf[BUFLEN];
- char *obp = obuf;
- char rbuf[BUFLEN];
- char *rbp = rbuf;
- int vflag;
- int pflag;
- int ttyf;
- struct sgttyb ottyb;
- struct sgttyb consb;
- struct tchars otc;
- struct ltchars oltc;
- short coflags;
- int ttydone;
- int consdone;
- char *myname;
- char **argp;
- int numargs;
- char lockfile[BUFLEN];
- int locked;
- int undef;
- char *dialdir;
- int baud = -1;
- int bdconst[] = {
- B50, B75, B110, B134, B150, B200, B300, B600,
- B1200, B1800, B2400, B4800, B9600, EXTA, EXTB
- };
- int bdnum[] = {
- 50, 75, 110, 134, 150, 200, 300, 600,
- 1200, 1800, 2400, 4800, 9600, 19200, 38400, 0
- };
-
- #define S_STATE 1
-
- struct state {
- struct state *s_next;
- char *s_string;
- int s_len;
- char *s_name;
- char *s_output;
- int s_outlen;
- struct timeval s_timeout;
- struct timeval s_delay;
- int s_stat;
- } *stab;
-
- extern errno;
- extern char **environ;
- char *malloc(), *alloc(), *memsav(), *getenv(), *tgetstr(), *fnexpand();
- struct state *enter(), *receive(), *next_state(), *user();
- struct passwd *getpwnam(), *getpwuid();
- long atol();
-
- main (ac, av) char **av; {
- register struct state *next;
- register char *p;
- int Exit();
-
- myname = ac < 1 ? "dial" : av[0];
- if (ac < 1) {
- help: err ("Use: %s [-v] [-p] [-lline] [script-file [args...]]\n", myname);
- }
- while (--ac) {
- p = *++av;
- if (*p == '-' && p[1] != '\0') {
- if (*++p == 'v') {
- ++vflag;
- } else if (*p == 'p') {
- ++pflag;
- } else if (*p == 'l') {
- line = ++p;
- } else goto help;
- } else if (!script) {
- script = p;
- } else {
- argp = av;
- numargs = ac;
- break;
- }
- }
- if (script) {
- dialdir = getenv ("DIALDIR");
- get_script ();
- check_script ();
- }
- if (!line || line[0] == '\0')
- err ("No tty line specified.\n");
- signal (SIGHUP, Exit);
- signal (SIGINT, Exit);
- open_line ();
- if (!stab)
- say ("Connected.\n");
- set_tty ();
- next = (stab ? stab : user ());
- while (next != EXIT)
- next = enter (next);
- if (vflag)
- say ("State `exit' -- done.\n");
- Exit (0);
- }
-
- get_script () {
- FILE *f = NULL;
- char fn[BUFLEN], lbuf[BUFLEN], buf[BUFLEN];
- register char *p, *s;
- register struct state *sp, *tmp;
- register c, delim, lno = 0;
- struct stat st;
-
- if (!strcmp (script, "-")) {
- f = stdin;
- script = "stdin";
- } else {
- if (stat (script, &st) || (st.st_mode & S_IFMT) == S_IFDIR
- || (f = fopen (script, "r")) == NULL) {
- if (dialdir) {
- sprintf (fn, "%s/%s", dialdir, script);
- f = fopen (fn, "r");
- }
- if (f == NULL) {
- sprintf (fn, "%s/%s", DIALLIB, script);
- if ((f = fopen (fn, "r")) == NULL)
- err ("Cannot open `%s'.\n", script);
- }
- }
- }
- while (1) {
- ++lno;
- if (fgetstr (f, lbuf, BUFLEN-1) == EOF)
- break;
- if (!expand_args (lbuf, buf)) goto error;
- for (p = buf; ISSPACE(*p); ++p) ;
- if (*p == '#' || *p == '\0')
- continue;
- if (p == buf) {
- if (getkey (p))
- continue;
- } else if (!stab) goto error;
- tmp = (struct state *)alloc (sizeof (struct state));
- tmp->s_stat = (p == buf ? S_STATE : 0);
- for (s = p; *s && !ISSPACE(*s); ++s) ;
- if (*s == '\0') goto error;
- *s = '\0';
- tmp->s_name = memsav (p, s-p+1);
- for (++s; ISSPACE(*s); ++s) ;
- if (delim = (*s == '"')) ++s;
- for (p = s; *s && (delim ? *s != '"' : !ISSPACE(*s)); ++s) ;
- if (delim) {
- if (*s == '\0') goto error;
- *s++ = '\0';
- }
- c = *s;
- *s = '\0';
- tmp->s_len = cook (p, p);
- tmp->s_string = memsav (p, tmp->s_len);
- tmp->s_outlen = 0;
- tmp->s_timeout = timeout;
- tmp->s_delay = delay;
- if (c) {
- for (++s; ISSPACE(*s); ++s) ;
- if (*s) {
- if (delim = (*s == '"')) ++s;
- for (p = s; *s && (delim ? *s != '"' : !ISSPACE(*s)); ++s) ;
- if (delim) {
- if (*s == '\0') goto error;
- *s++ = '\0';
- }
- *s = '\0';
- if (tmp->s_stat & S_STATE) {
- if (!set_par (tmp, p)) goto error;
- } else {
- tmp->s_outlen = cook (p, p);
- tmp->s_output = memsav (p, tmp->s_outlen);
- }
- }
- }
- if (stab)
- sp = sp->s_next = tmp;
- else
- stab = sp = tmp;
- sp->s_next = 0;
- }
- fclose (f);
- return;
- error:
- err ("%s: syntax error in line %d.\n", script, lno);
- }
-
- expand_args (from, to) char *from, *to; {
- register char *p, *q, *t, *s = to, *endp = to+BUFLEN-1;
- register n;
- FILE *f;
-
- for (p = from; *p; ++p) {
- if (s == endp)
- break;
- if (*p == '$') {
- if (p > from && p[-1] == '\\') {
- s[-1] = '$';
- } else if (ISDEC(p[1])) {
- if (*++p - '0' <= numargs) {
- for (q = argp[*p - '0']; s < endp && *q; *s++ = *q++)
- ;
- }
- } else if (p[1] == '*') {
- for (n = 0; n < numargs; ++n) {
- for (q = argp[n]; s < endp && *q; *s++ = *q++)
- ;
- if (n < numargs-1 && s < endp)
- *s++ = ' ';
- }
- ++p;
- } else if (p[1] == '{') {
- ++p;
- for (q = ++p; *p && *p != '}'; ++p) ;
- if (*p == '\0') return (0);
- *p = '\0';
- if (*q == '~' || *q == '/') {
- t = fnexpand (q);
- if ((f = fopen (t, "r")) == NULL)
- err ("Cannot open `%s'.\n", t);
- while ((n = getc (f)) != EOF && s < endp)
- *s++ = n;
- fclose (f);
- } else if (t = getenv (q)) {
- while (s < endp && *t)
- *s++ = *t++;
- }
- } else *s++ = '$';
- } else *s++ = *p;
- }
- *s = '\0';
- return (1);
- }
-
- char *fnexpand (s) char *s; {
- static char buf[BUFLEN];
- struct passwd *pw;
- register char c, *p, *q = s+1;
-
- if (*s != '~')
- return (s);
- for (p = q; (c = *p) && c != '/'; ++p) ;
- *p = '\0';
- if (p == q) {
- if (!(pw = getpwuid (getuid ())))
- err ("Cannot get home directory.\n");
- } else if (!(pw = getpwnam (q)))
- err ("Unknown user: %s.\n", q);
- if (c) {
- sprintf (buf, "%s/%s", pw->pw_dir, ++p);
- return (buf);
- } else return (pw->pw_dir);
- }
-
- set_par (sp, s) struct state *sp; char *s; {
- register char *p;
-
- for (p = s; *p && *p != ','; ++p) ;
- if (*p == ',')
- *p++ = '\0';
- if (*s) {
- if (!isnum (s)) return (0);
- set_time (&sp->s_timeout, s);
- }
- if (*p) {
- if (!isnum (p)) return (0);
- set_time (&sp->s_delay, p);
- }
- return (1);
- }
-
- set_time (tp, s) struct timeval *tp; char *s; {
- register char *p;
- register long div;
-
- for (p = s; *p && *p != '.'; ++p) ;
- if (*p == '.')
- *p++ = '\0';
- tp->tv_sec = atoi (s);
- if (p) {
- p[6] = '\0';
- tp->tv_usec = atol (p);
- div = 1;
- while (*p) {
- div *= 10;
- ++p;
- }
- tp->tv_usec *= 1000000L / div;
- } else tp->tv_usec = 0;
- }
-
- getkey (s) char *s; {
- register char *k, *p, *q;
-
- for (p = s; *p; ++p)
- if (*p == '=' && !(p > s && p[-1] == '\\'))
- break;
- if (*p == '\0') return (0);
- for (*p++ = '\0'; ISSPACE(*p); ++p) ;
- for (q = p; *q && !ISSPACE(*q); ++q) ;
- *q = '\0';
- for (k = s; *k && !ISSPACE(*k); ++k) ;
- *k = '\0';
- if (!strcmp (s, "line")) {
- if (*p == '\0')
- err ("Bad value for keyword `line'.\n");
- if (!line)
- line = memsav (p, strlen (p)+1);
- } else if (!strcmp (s, "timeout")) {
- if (!isnum (p))
- err ("Bad value for keyword `timeout'.\n");
- set_time (&timeout, p);
- } else if (!strcmp (s, "delay")) {
- if (!isnum (p))
- err ("Bad value for keyword `delay'.\n");
- set_time (&delay, p);
- } else if (!strcmp (s, "escape")) {
- if (strlen (p) != 1)
- err ("Bad value for keyword `escape'.\n");
- escape = *p & 0177;
- } else if (!strcmp (s, "prompt")) {
- prompt = memsav (p, strlen (p)+1);
- } else err ("Invalid keyword `%s'.\n", s);
- return (1);
- }
-
- check_script () {
- register struct state *sp;
-
- for (sp = stab; sp; sp = sp->s_next) {
- if (!(sp->s_stat & S_STATE)) continue;
- if (!sp->s_next || (sp->s_next->s_stat & S_STATE))
- err ("No transitions for state `%s'.\n", sp->s_name);
- }
- }
-
- struct state *enter (sp) struct state *sp; {
- if (vflag)
- say ("Entering state %s.\n", sp->s_name);
- if (sp->s_delay.tv_sec || sp->s_delay.tv_usec) {
- flushbuf ();
- if (select (0, (int *)0, (int *)0, (int *)0, &sp->s_delay) == -1) {
- perror ("select");
- Exit (1);
- }
- }
- sendstr (sp->s_string, sp->s_len);
- return (receive (sp));
- }
-
- sendstr (sbuf, len) char *sbuf; register len; {
- char buf[BUFLEN], ibuf[BUFLEN];
- register char *s, *p, *t;
- register n;
- char c;
-
- for (s = sbuf, p = buf; len && p < buf+BUFLEN; --len, ++p, ++s) {
- if (*s == '\\') {
- if (s[1] == '\\') {
- *p = '\\'; ++s;
- } else if (s[1] == '<' || s[1] == '?') {
- ++s;
- flushbuf ();
- if (*s == '<') {
- n = Read (0, ibuf, BUFLEN);
- c = '\n';
- } else {
- set_cons ();
- for (t = ibuf, n = 0; n < BUFLEN; ++n, ++t) {
- Read (0, t, 1);
- if (*t == '\r') break;
- }
- reset_cons ();
- if (!pflag)
- say ("\n");
- c = '\r';
- }
- for (t = ibuf; n && *t != c && p < buf+BUFLEN; --n)
- *p++ = *t++;
- } else *p = '\\';
- } else *p = *s;
- }
- if (vflag) {
- say ("Sending \""); prstr (buf, p-buf); say ("\".\n");
- }
- if (p > buf)
- Write (ttyf, buf, p-buf);
- }
-
- fill (sp) struct state *sp; {
- register i;
- register char *p;
- int rbits = (1 << ttyf);
-
- if (rbp == rbuf+BUFLEN)
- err ("Pattern space overflow.\n");
- flushbuf ();
- i = select (ttyf+1, &rbits, (int *)0, (int *)0, &sp->s_timeout);
- if (i == -1) {
- perror ("select");
- Exit (1);
- }
- if (i == 0) {
- if (vflag)
- say (" Got time-out.\n");
- return (0);
- }
- i = Read (ttyf, rbp, rbuf+BUFLEN-rbp);
- p = rbp;
- do {
- *p++ &= 0177;
- } while (--i);
- if (vflag) {
- say (" Got \""); prstr (rbp, p-rbp); say ("\".\n");
- }
- if (pflag)
- Write (1, rbp, p-rbp);
- rbp = p;
- return (1);
- }
-
- shift (n) {
- bcopy (rbuf+n, rbuf, rbp-rbuf-n);
- rbp -=n;
- }
-
- struct state *receive (sp) struct state *sp; {
- register struct state *p, *fullp, *partp;
- register r, mlen, dofill = 0;
- int len;
-
- while (1) {
- if ((rbp == rbuf || dofill) && !fill (sp)) {
- rbp = rbuf;
- for (p = sp->s_next; p && !(p->s_stat & S_STATE); p = p->s_next) {
- if (p->s_len == 1 && p->s_string[0] == '#') {
- output (p, (char *)0, 0);
- return (next_state (p->s_name, 1));
- }
- }
- return (EXIT);
- }
- dofill = 0;
- fullp = partp = 0;
- for (p = sp->s_next; p && !(p->s_stat & S_STATE); p = p->s_next) {
- if (p->s_len == 1 && p->s_string[0] == '#')
- continue;
- r = match (rbuf, rbp-rbuf, p->s_string, p->s_len, &len);
- if (r == FULL && !fullp) {
- fullp = p;
- mlen = len;
- } else if (r == PART && !partp) {
- partp = p;
- }
- }
- if (!fullp && !partp) {
- shift (1);
- if (vflag)
- say ("No match -- shift(1).\n");
- continue;
- }
- if (fullp && !partp) {
- if (fullp->s_len == 1 && fullp->s_string[0] == '*')
- mlen = amatch (sp);
- if (vflag)
- say ("Full match <%s> -- shift(%d).\n", fullp->s_name, mlen);
- output (fullp, rbuf, mlen);
- shift (mlen);
- return (next_state (fullp->s_name, 1));
- }
- if (vflag)
- say ("Partial match <%s>.\n", partp->s_name);
- dofill = 1;
- }
- }
-
- amatch (ap) struct state *ap; {
- register struct state *sp;
- register char *p;
- int notused;
-
- for (p = rbuf; p < rbp; ++p) {
- for (sp = ap->s_next; sp && !(sp->s_stat & S_STATE); sp = sp->s_next) {
- if (sp->s_len == 1 &&
- (sp->s_string[0] == '#' || sp->s_string[0] == '*'))
- continue;
- if (match (p, rbp-p, sp->s_string, sp->s_len, ¬used) != NONE)
- goto done;
- }
- }
- done:
- return (p-rbuf);
- }
-
- match (s, slen, t, tlen, len) register char *s, *t; register slen, tlen;
- int *len; {
- register otlen = tlen, oslen = slen;
-
- if (tlen == 1 && t[0] == '*')
- return (FULL);
- while (tlen) {
- if (!slen)
- return (PART);
- if (*t != '\\') {
- if (*s != *t)
- if (*t != '.' || (tlen < otlen && t[-1] == '\\'))
- return (NONE);
- ++s; --slen;
- }
- ++t; --tlen;
- }
- *len = oslen-slen;
- return (FULL);
- }
-
- struct state *next_state (s, noise) char *s; {
- register struct state *sp;
-
- undef = 0;
- if (!strcmp (s, "exit"))
- return (EXIT);
- if (!strcmp (s, "user")) {
- return (user ());
- }
- for (sp = stab; sp; sp = sp->s_next)
- if ((sp->s_stat & S_STATE) && !strcmp (sp->s_name, s))
- return (sp);
- ++undef;
- if (noise)
- err ("State `%s' not found in script.\n", s);
- #ifdef lint
- return (sp);
- #endif
- }
-
- struct state *user () {
- char c, buf[BUFLEN];
- register n, l, gotesc = 0;
- int rbits;
- register char *p, *s;
- register struct state *sp;
-
- flushbuf ();
- signal (SIGINT, SIG_IGN);
- set_cons ();
- while (1) {
- rbits = (1 << 0) | (1 << ttyf);
- n = select (ttyf+1, &rbits, (int *)0, (int *)0, (struct timeval *)0);
- if (n == -1) {
- perror ("select");
- Exit (1);
- }
- if (rbits & (1 << 0)) {
- if (gotesc) {
- Read (0, &c, 1);
- if (c == '.' || c == otc.t_eofc) {
- reset_cons ();
- say ("\nConnection closed.\n");
- return (EXIT);
- } else if (c == oltc.t_suspc) {
- reset_cons ();
- kill (getpid (), SIGTSTP);
- set_cons ();
- } else if (c == '!') {
- reset_cons ();
- say ("\n");
- if (system ((s = getenv ("SHELL")) ? s : "/bin/sh") == 127)
- say ("Cannot execute shell.\n");
- else say ("\n!\n");
- set_cons ();
- } else if (c == '?') {
- reset_cons ();
- print_help ();
- set_cons ();
- } else if (c == ' ' || c == '\r') {
- Write (1, prompt, strlen (prompt));
- reset_cons ();
- if ((n = Read (0, buf, BUFLEN)) > 1) {
- buf[n-1] = '\0';
- for (p = buf; ISSPACE(*p); ++p) ;
- for (s = p; *s && !ISSPACE(*s); ++s) ;
- *s = '\0';
- if (s > p) {
- sp = next_state (p, 0);
- if (undef) {
- say ("`"); prstr (p, s-p);
- say ("' is undefined.\n");
- } else {
- signal (SIGINT, Exit);
- return (sp);
- }
- }
- }
- set_cons ();
- } else Write (ttyf, &c, 1);
- gotesc = 0;
- } else {
- n = Read (0, buf, BUFLEN);
- for (p = buf, l = n; l && *p != escape; ++p, --l) ;
- if (l) {
- if (p > buf)
- Write (ttyf, buf, p-buf);
- ++gotesc;
- while (--l)
- ioctl (0, TIOCSTI, ++p);
- } else Write (ttyf, buf, n);
- }
- }
- if (rbits & (1 << ttyf))
- Write (1, buf, Read (ttyf, buf, BUFLEN));
- }
- }
-
- print_help () {
- char e[3];
-
- if (escape >= ' ' && escape <= '~')
- sprintf (e, "%c", escape);
- else
- sprintf (e, "^%c", escape ^ '@');
- say ("\n%s. close connection and terminate.\n", e);
- say ("%s! fork a shell.\n", e);
- say ("%s^Z suspend %s.\n", e, myname);
- say ("%s<CR>\n", e);
- say ("%s<SPACE> leave interactive mode and enter new state.\n", e);
- }
-
- output (sp, buf, len) struct state *sp; char *buf; {
- char tbuf[BUFLEN];
- register char *s = sp->s_output, *p = tbuf;
- register i, olen = sp->s_outlen;
-
- if (pflag || !olen)
- return;
- while (olen) {
- if (p == tbuf+BUFLEN) break;
- if (*s == '&' && !(s > sp->s_output && s[-1] == '\\')) {
- for (i = 0; i < len && p < tbuf+BUFLEN; ++i)
- *p++ = buf[i];
- } else if (*s != '\\') *p++ = *s;
- ++s;
- --olen;
- }
- if (vflag) {
- say ("Print \""); prstr (tbuf, p-tbuf); say ("\".\n");
- } else {
- outbuf (tbuf, p-tbuf);
- }
- }
-
- outbuf (buf, len) register char *buf; register len; {
- register l;
-
- again:
- l = obuf + BUFLEN - obp;
- if (l > len) l = len;
- bcopy (buf, obp, l);
- obp += l;
- if (len -= l) {
- flushbuf ();
- buf += l;
- goto again;
- }
- }
-
- flushbuf () {
- if (obp > obuf) {
- Write (1, obuf, obp-obuf);
- obp = obuf;
- }
- }
-
- open_line () {
- char fn[BUFLEN];
- char tbuf[1024], cbuf[1024];
- int i, f;
- struct stat statbuf;
- char **oldenv, *envp[2], *p, *pp = cbuf;
-
- sprintf (fn, line[0] == '/' ? "%s" : "/dev/%s", line);
- if (stat (fn, &statbuf) == -1 && errno == ENOENT) {
- sprintf (tbuf, "TERMCAP=%s", REMOTE);
- envp[0] = memsav (tbuf, strlen (tbuf));
- envp[1] = 0;
- oldenv = environ;
- environ = envp;
- if ((i = tgetent (tbuf, line)) == -1)
- err ("Cannot open `%s'.\n", REMOTE);
- else if (i == 0)
- err ("`%s': no such line or remote system.\n", line);
- if ((p = tgetstr ("dv", &pp)) == 0)
- err ("No `dv' capability for system `%s'.\n", line);
- strcpy (fn, p);
- if ((baud = tgetnum ("br")) != -1) {
- for (i = 0; bdnum[i] && baud != bdnum[i]; ++i) ;
- if (!bdnum[i])
- err ("Invalid baud rate %d for system `%s'.\n", baud, line);
- baud = bdconst[i];
- }
- environ = oldenv;
- }
- if (!strncmp (fn, "/dev/", 5)) {
- sprintf (lockfile, LOCKFN, fn+5);
- if ((f = open (lockfile, O_CREAT|O_EXCL, 0)) == -1) {
- if (errno == EEXIST)
- err ("`%s' is already in use.\n", line);
- perror (lockfile);
- Exit (1);
- }
- locked++;
- close (f);
- }
- if ((ttyf = open (fn, O_RDWR)) == -1) {
- perror (line);
- Exit (1);
- }
- ioctl (ttyf, TIOCHPCL, (char *)0); /* Hang up phone on last close. */
- }
-
- set_cons () {
- struct tchars tc;
- struct ltchars ltc;
-
- ioctl (0, TIOCGETP, &consb);
- coflags = consb.sg_flags;
- consb.sg_flags &= ~ECHO;
- consb.sg_flags &= ~CRMOD;
- consb.sg_flags |= CBREAK;
- ioctl (0, TIOCSETP, &consb);
- ioctl (0, TIOCGETC, &tc);
- otc = tc;
- tc.t_intrc = tc.t_quitc = -1;
- ioctl (0, TIOCSETC, &tc);
- ioctl (0, TIOCGLTC, <c);
- oltc = ltc;
- ltc.t_suspc = ltc.t_dsuspc = ltc.t_lnextc = -1;
- ioctl (0, TIOCSLTC, <c);
- ++consdone;
- }
-
- reset_cons () {
- if (consdone) {
- consb.sg_flags = coflags;
- ioctl (0, TIOCSETP, &consb);
- ioctl (0, TIOCSETC, &otc);
- ioctl (0, TIOCSLTC, &oltc);
- consdone = 0;
- }
- }
-
- set_tty () {
- struct sgttyb ttyb;
- ioctl (ttyf, TIOCGETP, &ttyb);
- ottyb = ttyb;
- ttyb.sg_flags &= ~ECHO;
- ttyb.sg_flags &= ~CRMOD;
- ttyb.sg_flags |= (RAW|TANDEM);
- if (baud != -1)
- ttyb.sg_ispeed = ttyb.sg_ospeed = baud;
- ioctl (ttyf, TIOCSETP, &ttyb);
- ++ttydone;
- }
-
- reset_tty () {
- if (ttydone)
- ioctl (ttyf, TIOCSETP, &ottyb);
- }
-
- Exit (c) {
- flushbuf ();
- reset_tty ();
- reset_cons ();
- if (locked) {
- if (unlink (lockfile) == -1)
- perror (lockfile);
- }
- exit (c);
- }
-
- Write (f, buf, len) char *buf; {
- if (write (f, buf, len) != len) {
- if (errno == EIO)
- err ("Lost line.\n");
- else {
- perror ("write");
- Exit (1);
- }
- }
- }
-
- Read (f, buf, len) char *buf; {
- register n;
- if ((n = read (f, buf, len)) == -1) {
- if (errno == EIO)
- err ("Lost line.\n");
- else {
- perror ("read");
- Exit (1);
- }
- }
- if (n == 0)
- err ("EOF on tty.\n");
- return (n);
- }
-
- char *memsav (s, len) char *s; {
- register char *p;
- p = alloc (len);
- bcopy (s, p, len);
- return (p);
- }
-
- char *alloc (n) {
- register char *p;
- if ((p = malloc (n)) == 0)
- err ("Out of memory.\n");
- return (p);
- }
-
- cook (dst, src) char *dst, *src; {
- register char *p, *t;
- register n, c;
-
- for (p = dst, t = src; c = *t; ++t) {
- if (c == '\\') {
- if (ISOCT(t[1])) {
- for (n = c = 0; n < 3; ++n) {
- c = (c << 3) | (*++t - '0');
- if (!ISOCT(t[1]))
- break;
- }
- } else {
- if (t[1] == 'r') {
- c = '\r'; ++t;
- } else if (t[1] == 'n') {
- c = '\n'; ++t;
- } else if (t[1] == '\0')
- break;
- }
- }
- *p = c; ++p;
- }
- return (p - dst);
- }
-
- prstr (s, len) char *s; int len; {
- register c, l;
-
- for (l = len; l; --l) {
- if ((c = *s++) == '\r')
- say ("\\r");
- else if (c == '\n')
- say ("\\n");
- else if (c < ' ' || c > '~')
- say ("\\%03o", c & 0377);
- else say ("%c", c);
- }
- }
-
- fgetstr (f, buf, len) register FILE *f; char *buf; {
- register c, n;
- register char *p = buf;
-
- n = len;
- while ((c = getc (f)) != EOF && c != '\n')
- if (n) {
- --n; *p++ = c;
- }
- *p = '\0';
- return (c == EOF && p == buf ? EOF : p - buf);
- }
-
- isnum (s) register char *s; {
- register point = 0;
-
- if (*s == '\0') return (0);
- for ( ; *s; ++s) {
- if (*s == '.') {
- if (point) return (0);
- ++point;
- } else if (!ISDEC(*s)) return (0);
- }
- return (1);
- }
-
- /*VARARGS1*/
- say (fmt, p1, p2, p3, p4) char *fmt; {
- fprintf (stdout, fmt, p1, p2, p3, p4);
- }
-
- /*VARARGS1*/
- err (fmt, p1, p2, p3, p4) char *fmt; {
- fprintf (stderr, fmt, p1, p2, p3, p4);
- Exit (1);
- }
- SHAR_EOF
- if test 21088 -ne "`wc -c < 'dial.c'`"
- then
- echo shar: error transmitting "'dial.c'" '(should have been 21088 characters)'
- fi
- fi # end of overwriting check
- # End of shell archive
- exit 0
-
-